package com.cheer.service.impl;

import cn.hutool.core.lang.UUID;
import com.cheer.common.ResultEnum;
import com.cheer.dao.AccessTokenCacheDao;
import com.cheer.exception.ServiceException;
import com.cheer.service.AuthService;
import com.cheer.vo.ShareTicketVO;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Stopwatch;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.concurrent.TimeUnit;

/**
 * @author cheer
 */
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {

    @Value("${weixin.appId}")
    private String appid;

    @Value("${weixin.appSecret}")
    private String secret;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private AccessTokenCacheDao accessTokenCacheDao;

    private Boolean dev;

    @Value("${spring.profiles.active}")
    public void setDev(String dev) {
        if("dev".equals(dev)){
            this.dev = true;
        }else{
            this.dev = false;
        }
    }

    @Override
    public void login(String code, HttpSession session) {
        AccessTokenResponse accessToken;
        // 线上环境
        if(dev == false){
            accessToken = getAccessTokenByCode(code);
        }else{
            // 测试环境
            accessToken = new AccessTokenResponse();
            accessToken.setOpenid("15910470048");
        }

        // 查询用户信息，是否关注公众号，头像
        /*String appAccessToken = accessTokenCacheDao.getAccessToken();
        if (StringUtils.isBlank(appAccessToken)) {
            throw new ServiceException("登录失败，请重新访问");
        }

        UserInfoResponse userInfo = getUserInfoByAccessToken(appAccessToken, accessToken.getOpenid(), 3);
        if (userInfo == null) {
            throw new ServiceException("登录失败，请重新访问");
        }
        if (userInfo.getSubscribe() != 1) {
            throw new ServiceException(ResultEnum.ACCOUNTS);
        }*/

        session.setAttribute("openid", accessToken.getOpenid());
    }


    @Override
    public String getOpenid(HttpSession session) {
        if (session.getAttribute("openid") == null) {
            throw new ServiceException("会话已超时，请重新登录");
        }
        return (String) session.getAttribute("openid");
    }

    @Override
    public ShareTicketVO share(String url) {
        try {
            url = URLDecoder.decode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        }

        String ticket = accessTokenCacheDao.getTicket();
        String noncestr = UUID.randomUUID().toString(true).substring(0, 16);
        long timestamp = System.currentTimeMillis() / 1000;
        // 将参数排序并拼接字符串
        String str = "jsapi_ticket=" + ticket + "&noncestr=" + noncestr + "&timestamp=" + timestamp + "&url=" + url;
        String signature = DigestUtils.sha1Hex(str);

        ShareTicketVO shareTicketVO = new ShareTicketVO();
        shareTicketVO.setAppId(appid);
        shareTicketVO.setTimestamp(timestamp);
        shareTicketVO.setNonceStr(noncestr);
        shareTicketVO.setSignature(signature);
        return shareTicketVO;
    }

    private AccessTokenResponse getAccessTokenByCode(String code) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&" +
                "code=%s&grant_type=authorization_code", appid, secret, code);

        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                AccessTokenResponse response = objectMapper.readValue(responseEntity.getBody(), AccessTokenResponse.class);
                if (response.getAccess_token() != null && response.getOpenid() != null) {
                    return response;
                }
            }
            log.warn("get access token failed, response: {}, code {}  cost {}", responseEntity, code, stopwatch.elapsed(TimeUnit.MILLISECONDS));
        } catch (Exception e) {
            log.warn("get access token failed", e);
        }
        throw new ServiceException("登录失败，请重新访问");
    }

    private String getAppAccessToken() {
        String url = String.format("https://api.weixin.qq.com/cgi-bin/token?appid=%s&secret=%s&" +
                "grant_type=client_credential", appid, secret);

        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                AccessTokenResponse response = objectMapper.readValue(responseEntity.getBody(), AccessTokenResponse.class);
                if (response.getAccess_token() != null) {
                    return response.getAccess_token();
                }
            }
            log.error("get access token failed, response: {}", responseEntity);
        } catch (Exception e) {
            log.error("get access token failed", e);
        }
        return null;
    }

    private String getAppTicket(String accessToken) {
        String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi";

        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                TicketResponse response = objectMapper.readValue(responseEntity.getBody(), TicketResponse.class);
                if (response.getTicket() != null) {
                    return response.getTicket();
                }
            }
            log.error("get ticket failed, response: {}", responseEntity);
        } catch (Exception e) {
            log.error("get ticket failed", e);
        }
        return null;
    }

    private UserInfoResponse getUserInfoByAccessToken(String accessToken, String openid, Integer retryCount) {
        Stopwatch stopwatch = Stopwatch.createStarted();
        String url = String.format("https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN",
                accessToken, openid);

        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                UserInfoResponse response = objectMapper.readValue(responseEntity.getBody(), UserInfoResponse.class);
                if (response.getSubscribe() != null) {
                    return response;
                } else if (retryCount > 0) {
                    return getUserInfoByAccessToken(accessToken, openid, retryCount - 1);
                }
            } else {
                log.warn("get user info failed, code {}  cost {}", responseEntity, stopwatch.elapsed(TimeUnit.MILLISECONDS));
            }
        } catch (Exception e) {
            log.error("get user info failed", e);
        }
        return null;
    }

    @Data
    private static class AccessTokenResponse {

        private String access_token;

        private Integer expires_in;

        private String refresh_token;

        private String openid;

        private String scope;
    }

    @Data
    private static class TicketResponse {

        private String errcode;

        private String errmsg;

        private String ticket;

        private Integer expires_in;
    }

    @Data
    private static class UserInfoResponse {

        private Integer subscribe;

        private String openid;

        private String nickname;

        private Integer sex;

        private String language;

//        private String city;
//
//        private String province;
//
//        private String country;

        private String headimgurl;
    }

}
